package io.gitee.tuo_tuo.jfinaldevassistance.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Record;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.*;

/**
 * 类名称：
 * 描述:
 * 版权: Copyright (c) 2018
 * 公司:
 * 作者: tuotuo
 * 版本: 1.0
 * 创建日期: 2018/7/11
 * 创建时间: 13:32
 */
public class ModelUtil {

	/**
	 * 对比model的两个实例
	 * @param one  db one
	 * @param two  db two
	 * @param excludeColumns  排除对比的字段
	 * @return 对比结果
	 */
	public static <M extends Model> JSONArray compare(M one, M two, String... excludeColumns){
		if(one.getClass() != two.getClass()){
			throw new RuntimeException("one 和 two 应该是Model的同一子类！");
		}
		JSONArray array = new JSONArray();
		Map<String, String> modelColumnsDesc = getModelColumnsDesc(one.getClass());
		modelColumnsDesc.forEach((field, fieldDesc) ->{
			boolean isExclude = Arrays.asList(excludeColumns).stream().anyMatch(column -> column.equals(field));
			if(isExclude){
				return;
			}
			Object oneColumnValue = one.get(field);
			Object twoColumnValue = two.get(field);
			if(oneColumnValue instanceof Date){
				oneColumnValue = DateUtil.format((Date)oneColumnValue, "yyyyMMdd HH:mm:ss");
			}
			if(twoColumnValue instanceof Date){
				twoColumnValue = DateUtil.format((Date)twoColumnValue, "yyyyMMdd HH:mm:ss");
			}
			if(ObjectUtils.notEqual(oneColumnValue, twoColumnValue)){
				JSONObject jsonObject = new JSONObject();
				jsonObject.put("field", field);
				jsonObject.put("field_desc", fieldDesc);
				jsonObject.put("one_value", oneColumnValue);
				jsonObject.put("two_value", twoColumnValue);
				array.add(jsonObject);
			}
		});
		return array;
	}


	/**
	 * 获取model对应的数据库表名
	 * @param modelClass
	 * @return
	 */
	public static <M extends Model> String tableName(Class<M> modelClass) {
		String name = modelClass.getSimpleName();
		StringBuilder result = new StringBuilder("t_");
		if (name != null && name.length() > 0) {
			result.append(name.substring(0, 1).toLowerCase());
			for (int i = 1; i < name.length(); i++) {
				String s = name.substring(i, i + 1);
				if (s.equals(s.toUpperCase()) && !Character.isDigit(s.charAt(0))) {
					result.append("_");
				}
				result.append(s.toLowerCase());
			}
		}
		return result.toString();
	}

	/**
	 * 将model转化为map
	 * @param model
	 * @return
	 */
	public static Map<String, Object> toMap(Model model){
		Map<String, Object> attrs = (Map<String, Object>)ReflectUtil.getFieldValue(model, "attrs");
		return attrs;
	}

	/**
	 * 抽取Model中的字段
	 * @param model
	 * @param fieldList <要抽取的字段名, 字段别名>
	 * @return
	 */
	public static Map<String, Object> extract(Model model, List<Field> fieldList){
		Map<String, Object> result = new LinkedHashMap<>();
		for (int i = 0; i < fieldList.size(); i ++){
			Field field = fieldList.get(i);
			Object fieldValue = getFieldValue(model, field.fieldName);
			if(field.transfer != null && fieldValue != null && NumberUtils.isParsable(fieldValue.toString())){
				fieldValue = field.transfer.transfer(Integer.parseInt(fieldValue.toString()));
			}
			result.put(field.fieldAlias, fieldValue);
		}
		return result;
	}

	public static class Field{
		public String fieldName;
		public String fieldAlias;
		public DicUtil.Transfer transfer;

		public Field(String fieldName, String fieldAlias){
			this(fieldName, fieldAlias, null);
		}

		public Field(String fieldName, String fieldAlias, DicUtil.Transfer transfer){
			this.fieldName = fieldName;
			this.fieldAlias = fieldAlias;
			this.transfer = transfer;
		}
	}


	/**
	 * 抽取Model中的字段
	 * @param
	 * @param fieldList <要抽取的字段>
	 * @return
	 */
	public static List<Map<String, Object>> extract(List<? extends Model> modelList, List<Field> fieldList){
		List<Map<String, Object>> result = new ArrayList<>(modelList.size());
		modelList.forEach(model -> {
			Map<String, Object> extract = extract(model, fieldList);
			result.add(extract);
		});
		return result;
	}

	/**
	 * 从model中取出字段值
	 * @param model
	 * @param key eg: protocolInfo.protocol_contract_price.***
	 * @return
	 */
	public static Object getFieldValue(Model model, String key){
		String[] strings = key.split("\\.");
		if(strings.length == 1) {
			return model.get(key);
		}
		Object fieldValue = null;
		for(int i = 0; i < strings.length; i++){
			fieldValue = model.get(strings[i]);
			if(i == strings.length - 1){
				return fieldValue;
			}else if(fieldValue instanceof Model){
				model = (Model) fieldValue;
				fieldValue = model.get(strings[i]);
			}else if(fieldValue instanceof List && ((List) fieldValue).size() > 0 && ((List) fieldValue).get(1) instanceof Model){
				model = (Model) ((List) fieldValue).get(1);
				fieldValue = model.get(strings[i]);
			}else{
				return null;
			}
		}
		return fieldValue;
	}

	/**
	 * 获取model对应数据库表字段的字段描述
	 * @param modelClass
	 * @return
	 */
	public static Map<String, String> getModelColumnsDesc(Class<? extends Model> modelClass){
		String tableName = tableName(modelClass);
		List<Record> records = Db.find("show full columns from " + tableName);
		Map columnDesc = new HashMap();
		records.forEach(item -> {
			String field = item.getStr("Field");
			String comment = item.getStr("Comment");
			columnDesc.put(field, comment);
		});
		return columnDesc;
	}

	public static Object getAttr(Model model, String key){
		String[] keys = key.split("\\.");
		Object attr = model.get(keys[0]);
		Record record = null;
		if(attr instanceof Record){
			record = (Record) attr;
		}else if(keys.length == 1){
			return attr;
		}else {
			return null;
		}
		for(int i = 1; i < keys.length; i++){
			attr = record.get(keys[i]);
			if(i == keys.length - 1){
				return attr;
			}
			if(attr instanceof Record){
				record = ((Record) attr);
			}else{
				return null;
			}
		}
		return attr;
	}
	/**
	 * 给List<? extends Model>中每一个model增加到对应的外键model，用来解决多表查询问题
	 * @param modelList
	 * @return 添加的foreignModel列表
	 */
	public static <M extends Model> List<M> addForeignModel(List<? extends Model> modelList, String key,
																		  Class<M> modelClass, String foreignKey){
		return addForeignModel(modelList, key, modelClass, foreignKey, null, null);
	}

	/**
	 * 给List<? extends Model>中每一个model增加到对应的外键model，用来解决多表查询问题
	 * @param modelList
	 * @param alias 添加的model的别名
	 * @return 添加的foreignModel列表
	 */
	public static <M extends Model> List<M> addForeignModel(List<? extends Model> modelList, String key,
																		  Class<M> modelClass, String foreignKey,
																		  String condition, String alias){
		// 创建实例
		M instance = null;
		try {
			instance = modelClass.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		if(modelList == null || modelList.size() == 0){
			return null;
		}
		Object[] objectIds = new Object[modelList.size()];
		Map<Object, List<Model>> projectMap = new HashMap<>();
		for(int i = 0; i < modelList.size(); i ++){
			Model model = modelList.get(i);
			Object value = getAttr(model, key);
			objectIds[i] = (value == null ? -1 : value);
			List<Model> models = projectMap.get(value);
			if(models == null){
				models = new ArrayList<>();
			}
			models.add(model);
			projectMap.put(value, models);
		}
		String tableName = tableName(modelClass);
		String sql = "select * from " + tableName
				+ " where "+ foreignKey +" in ("+ StringUtils.join(objectIds, ',') + ") "
				+ (condition == null ? "" : "and " + condition);

		List<M> foreignModelList = instance.find(sql);
		if(modelList.size() == 0){
			return foreignModelList;
		}

		String attrName;
		if(alias != null && !alias.equals("")){
			attrName = alias;
		}else{
			String simpleName = modelClass.getSimpleName();
			attrName = simpleName.substring(0,1).toLowerCase() + simpleName.substring(1,simpleName.length());
		}
		String finalAttrName = attrName;
		foreignModelList.forEach(item ->{
			Object value = item.get(foreignKey);
			List<Model> models = projectMap.get(value);
			models.forEach( model -> {
				Object originalValue = model.get(finalAttrName);
				if(originalValue != null){
					if(originalValue instanceof List){
						((List) originalValue).add(value);
					}else{
						ArrayList<Object> objects = new ArrayList<>();
						objects.add(originalValue);
						objects.add(item);
						model.put(finalAttrName, objects);
					}
				}else{
					model.put(finalAttrName, item);
				}
			});
		});
		return foreignModelList;
	}

}
